package main

import "fmt"
import "flag"
import "demoutil"
import "os"
import "strconv"
import "strings"
import "bufio"

// Distributed System
import "shardmaster"

//go run demo_server_sm.go

var DoTcp = flag.Bool("tcp", false, "communicate across tcp")
var DoAuto = flag.Bool("auto", true, "load hardcoded settings")

var sma []*shardmaster.ShardMaster
var mck *shardmaster.Clerk

func main() {
	Init()
	run := true
	execute := true
	err_msg := ""
	cmd := demoutil.Make_cmd()
	line := ""
	in := bufio.NewReader(os.Stdin)
	var err error
	for run {
		fmt.Printf(">>>")
		line, err = in.ReadString('\n')
		if err != nil {
			panic(err)
		} else {
			execute = cmd.ParseInput(line)
			run = cmd.GetStatus()
			if execute {
				err_msg = Execute(cmd.GetTodo())
				if err_msg != "" {
					fmt.Println("Error:" + err_msg)
				}
			}
		}
	}
}
func Init() {
	os.RemoveAll("/var/tmp/824-" + strconv.Itoa(os.Getpid()))
	fmt.Println("Welcome, ShardMaster :) ")
	flag.Parse()
	if *DoTcp {
		fmt.Println("connect with 'tcp'")
	} else {
		fmt.Println("Default: Connect with 'unix' instead of 'tcp'")
	}

	nmasters := 3
	var smh []string

	if *DoAuto {
		smh = make([]string, nmasters)
		sma = make([]*shardmaster.ShardMaster, nmasters)
		for i := 0; i < nmasters; i++ {
			smh[i] = demoutil.Port("demo_sm", i, *DoTcp)
		}
		for i := 0; i < nmasters; i++ {
			sma[i] = shardmaster.StartServer(smh, i)
		}

		//smh2 := demoutil.GetConn(smh, *DoTcp)
		//mck = shardmaster.MakeClerk(smh2)
		mck = shardmaster.MakeClerk(smh)
		fmt.Printf("Default: Start %v ShardMaster\n", nmasters)
	} else {
		panic("Haven't implement interactive manual input yet")
	}

}

func Execute(cc []string) string {
	err_msg := ""
	switch cc[0] {
	case "conn":
		_, servers := sma[0].GetConn()
		fmt.Println("Server Connection:")
		for i := 0; i < len(servers); i++ {
			fmt.Printf("Server %v:  %v\n", i, servers[i])
		}
	case "join":
		if len(cc) != 3 {
			err_msg = "join: need two arguments: group id, shardkv group servers"
		} else {
			gid, err := strconv.Atoi(cc[1])
			if err != nil {
				err_msg = "first arguments group id should be an integer"
			} else {
				replicas := strings.Split(cc[2], ",")
				mck.Join(int64(gid), replicas)
			}
		}
	case "leave":
		if len(cc) != 2 {
			err_msg = "leave: need one arguments: group id"
		} else {
			gid, err := strconv.Atoi(cc[1])
			if err != nil {
				err_msg = "first arguments group id should be an integer"
			} else {
				mck.Leave(int64(gid))
			}
		}
	case "cfg", "shard", "group":
		cfg := mck.Query(-1)
		fmt.Printf("Configuration Num: %v\n", cfg.Num)
		fmt.Println("------------")
		if cc[0][0] == 'c' || cc[0][0] == 'g' {
			for k, v := range cfg.Groups {
				fmt.Printf("Gid %v: %v\n", k, v)
			}
		}
		if cc[0][0] == 'c' || cc[0][0] == 's' {
			fmt.Println("Shard: ", cfg.Shards)
		}

	default:
		err_msg = "command not found"

	}
	return err_msg
}
